package com.demo.seckill.service.impl;

import com.demo.seckill.common.aop.ServiceLimit;
import com.demo.seckill.common.aop.ServiceLock;
import com.demo.seckill.common.dynamicquery.DynamicQuery;
import com.demo.seckill.common.entity.Result;
import com.demo.seckill.common.entity.Seckill;
import com.demo.seckill.common.entity.SuccessKilled;
import com.demo.seckill.common.enums.SeckillStatEnum;
import com.demo.seckill.repository.SeckillRepository;
import com.demo.seckill.service.ISeckillService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author libo
 * @ClassName SeckillServiceImpl
 * @date 2018/11/15 17:55
 */
@Service("seckillService")
public class SeckillServiceImpl implements ISeckillService {

    //互斥锁，默认false 不公平锁
    private Lock lock = new ReentrantLock(true);

    @Autowired
    private DynamicQuery dynamicQuery;

    @Autowired
    private SeckillRepository seckillRepository;

    @Override
    public List<Seckill> getSeckillList() {
        return seckillRepository.findAll();
    }

    @Override
    public Seckill getById(long seckillId) {
        return seckillRepository.findOne(seckillId);
    }

    @Override
    public Long getSeckillCount(long seckillId) {
        String nativeSql = "SELECT count(*) FROM success_killed WHERE seckill_id=?";
        Object object = dynamicQuery.nativeQueryObject(nativeSql,new Object[]{seckillId});
        return ((Number)object).longValue();
    }

    @Override
    @Transactional
    public void deleteSeckill(long seckillId) {
        String nativeSql  = "DELETE FROM  success_killed WHERE seckill_id=? ";
        dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
        nativeSql = "UPDATE seckill SET number =100 WHERE seckill_id=?";
        dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
    }

    @Override
    @ServiceLimit
    @Transactional
    public Result startSeckil(long seckillId, long userId) {
        //校验库存
        String nativeSql = "SELECT number FROM seckill where seckill_id=?";
        Object object = dynamicQuery.nativeQueryObject(nativeSql,new Object[]{seckillId});
        Long number = ((Number)object).longValue();
        if(number > 0){
            nativeSql = "UPDATE seckill  SET number=number-1 WHERE seckill_id=? and number >0;";
            int count = dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
            //创建订单
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState((short)0);
            killed.setCreateTime(new Timestamp(new Date().getTime()));
            dynamicQuery.save(killed);
            //支付
            return Result.ok(SeckillStatEnum.SUCCESS);
        }else{
            return  Result.error(SeckillStatEnum.END);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result startSeckilLock(long seckillId, long userId) {
        try {
            //由于提交事务在释放锁之后，另一个事务获取到未提交的数据，导致脏读，应该锁上移，所以还是会超卖
            lock.lock();
            String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
            Object object =  dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
            Long number =  ((Number) object).longValue();
            if(number > 0){
                nativeSql = "UPDATE seckill set number=number-1 where number>0 and seckill_id=?";
                dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{seckillId});
                SuccessKilled successKilled = new SuccessKilled();
                successKilled.setSeckillId(seckillId);
                successKilled.setUserId(userId);
                successKilled.setState(Short.parseShort(number+""));
                successKilled.setCreateTime(new Timestamp(new Date().getTime()));
                dynamicQuery.save(successKilled);
            }else {
                return  Result.error(SeckillStatEnum.END);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
        return Result.ok(SeckillStatEnum.SUCCESS);
    }

    @Override
    @ServiceLock
    @Transactional
    public Result startSeckilAopLock(long seckillId, long userId) {
        String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?";
        Object object =  dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId});
        Long number =  ((Number) object).longValue();
        if(number > 0){
            nativeSql = "UPDATE seckill set number=number-1 where number>0 and seckill_id=?";
            dynamicQuery.nativeExecuteUpdate(nativeSql,new Object[]{seckillId});
            SuccessKilled successKilled = new SuccessKilled();
            successKilled.setSeckillId(seckillId);
            successKilled.setUserId(userId);
            successKilled.setState(Short.parseShort(number+""));
            successKilled.setCreateTime(new Timestamp(new Date().getTime()));
            dynamicQuery.save(successKilled);
        }else {
            return  Result.error(SeckillStatEnum.END);
        }
        return Result.ok(SeckillStatEnum.SUCCESS);
    }

    @Override
    @ServiceLimit
    @Transactional
    public Result startSeckilDBPCC_ONE(long seckillId, long userId) {
        String nativeSql = "SELECT number FROM seckill WHERE seckill_id=? for update";
        Object object  = dynamicQuery.nativeQueryArray(nativeSql,new Object[]{seckillId});
        Long number =  ((Number) object).longValue();
        if(number>0){
            nativeSql = "UPDATE seckill  SET number=number-1 WHERE seckill_id=?";
            dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState((short)0);
            killed.setCreateTime(new Timestamp(new Date().getTime()));
            dynamicQuery.save(killed);
            return Result.ok(SeckillStatEnum.SUCCESS);
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }

    @Override
    @Transactional
    public Result startSeckilDBPCC_TWO(long seckillId, long userId) {
        //单用户抢购一件商品没有问题、但是抢购多件商品不建议这种写法
        String nativeSql = "UPDATE seckill  SET number=number-1 WHERE seckill_id=? AND number>0";//UPDATE锁表
        int count = dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId});
        if(count>0){
            SuccessKilled killed = new SuccessKilled();
            killed.setSeckillId(seckillId);
            killed.setUserId(userId);
            killed.setState((short)0);
            killed.setCreateTime(new Timestamp(new Date().getTime()));
            dynamicQuery.save(killed);
            return Result.ok(SeckillStatEnum.SUCCESS);
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }

    @Override
    @Transactional
    public Result startSeckilDBOCC(long seckillId, long userId, long number) {
        Seckill kill = seckillRepository.findOne(seckillId);
        if(kill.getNumber()>=number){//剩余的数量应该要大于等于秒杀的数量
            //乐观锁
            String nativeSql = "UPDATE seckill  SET number=number-?,version=version+1 WHERE seckill_id=? AND version = ?";
            int count = dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{number,seckillId,kill.getVersion()});
            if(count>0){
                SuccessKilled killed = new SuccessKilled();
                killed.setSeckillId(seckillId);
                killed.setUserId(userId);
                killed.setState((short)0);
                killed.setCreateTime(new Timestamp(new Date().getTime()));
                dynamicQuery.save(killed);
                return Result.ok(SeckillStatEnum.SUCCESS);
            }else{
                return Result.error(SeckillStatEnum.END);
            }
        }else{
            return Result.error(SeckillStatEnum.END);
        }
    }

    @Override
    public Result startSeckilTemplate(long seckillId, long userId, long number) {
        return null;
    }
}
